# Copyright (c) 2023 Presto Labs Pte. Ltd.
# Author: leon

import logging
from typing import List, Union
import re

import requests
import tornado.gen

import coin.flow.topic_map as topic_map
from coin.base.timestamp import get_timestamp
from coin.base.param_util import to_list
from coin.exchange.uniswap_base.uniswap_manager import UniswapManager
from coin.exchange.uniswap_base.rest.async_feed_client import UniswapAsyncFeedClient
from coin.exchange.uniswap_base.constants import PRODUCTION_RPC_URL
from coin.exchange.base.rest.task_scheduler import Scheduler


def deduce_version_from_symbol(symbol):
  splitted = re.split('\.|-', symbol)
  if len(splitted) == 2:
    return 2
  elif len(splitted) == 3:
    return 3
  else:
    raise ValueError(symbol)


class UniswapSubscriber(object):
  def __init__(
      self,
      queue,
      group_id: str,
      worker_id: str,
      symbol: Union[List[str], str],
      provider: str = PRODUCTION_RPC_URL['ethereum'],
      blockchain_name: str = 'ethereum',
      close_callback=None,
      logger=None,
  ):
    self._symbols = to_list(symbol)
    assert len(self._symbols) >= 1, self._symbols
    self._publisher = None
    self._scheduler = Scheduler()
    self._version = deduce_version_from_symbol(symbol[0])
    self._init_publisher(queue, group_id, worker_id)
    self._logger = logger or logging.getLogger(__name__)
    uniswap_mgr = UniswapManager(
        provider, version=self._version, blockchain_name=blockchain_name, logger=self._logger)
    self._feed_client = UniswapAsyncFeedClient(uniswap_mgr=uniswap_mgr, logger=self._logger)

  def _init_publisher(self, queue, group_id: str, worker_id: str):
    topic_expect_bbo = topic_map.search_topic_id(
        'uniswapv%s_rest_depth_%s%s' % (self._version, group_id, worker_id))[0]
    topic_trade = topic_map.search_topic_id(
        'uniswapv%s_rest_trade_%s%s' % (self._version, group_id, worker_id))[0]

    self._publisher = {
        'depth': queue.get_writer(topic_id=topic_expect_bbo[1]),
        'trade': queue.get_writer(topic_id=topic_trade[1]),
    }

  @tornado.gen.coroutine
  def _query_book(self, symbol):
    try:
      fut = self._feed_client.async_query_expect_bbo(symbol=symbol)
    except requests.exceptions.RequestException as e:
      self._logger.error(f'Query book error: {e}')
      return

    update = yield fut
    if update is not None:
      timestamp = get_timestamp()
      self._publisher['depth'].write(timestamp, update)
    else:
      self._logger.error('Query book error! No update.')

  @tornado.gen.coroutine
  def _query_trade(self, symbol):
    try:
      fut = self._feed_client.async_query_trades(symbol=symbol)
    except requests.exceptions.RequestException as e:
      self._logger.error(f'Query trade error: {e}')
      return

    update = yield fut
    if update is None:
      self._logger.error('Query trade error! No update.')
    elif update:
      timestamp = get_timestamp()
      self._publisher['trade'].write(timestamp, update)

  def start(self, ioloop, delay_time=0):
    self.stop()
    self._scheduler = Scheduler(ioloop)
    for symbol in self._symbols:
      self._scheduler.add_periodic_task(period=7,
                                        job_func=self._query_book,
                                        symbol=symbol)
      self._scheduler.add_periodic_task(period=7,
                                        job_func=self._query_trade,
                                        symbol=symbol)
    self._scheduler.start(later_td=delay_time)

  def stop(self):
    if self._scheduler is not None:
      self._scheduler.stop()
